﻿using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Validation;
using System.Diagnostics;
using System.Linq;
using DataTier.DataAccess.MainModule;
using DataTier.DataCore.UnitOfWork;

namespace DataTier.DataAccess.UnitOfWork
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly",
    Justification = "Inherited from IDbContext interface, which exists to support using.")]
    public class MainModuleUnitOfWork : IQueryableUnitOfWork
    {
        private readonly MainModuleContext _mainModuleContext;

        public MainModuleUnitOfWork(MainModuleContext mainModuleContext)
        {
            _mainModuleContext = mainModuleContext;
        }
        #region IQueryableUnitOfWork Members

        public DbSet<TEntity> CreateSet<TEntity>()
            where TEntity : class
        {
            return _mainModuleContext.Set<TEntity>();
        }

        public void Attach<TEntity>(TEntity item) where TEntity : class
        {
            //attach and set as unchanged
            _mainModuleContext.Entry<TEntity>(item).State = System.Data.EntityState.Unchanged;
        }

        public void SetModified<TEntity>(TEntity item)
            where TEntity : class
        {
            //this operation also attach item in object state manager
            _mainModuleContext.Entry<TEntity>(item).State = System.Data.EntityState.Modified;
        }

        public void SetEntityState<TEntity>(TEntity item, EntityState entityState)
           where TEntity : class
        {
            //this operation also attach item in object state manager
            _mainModuleContext.Entry<TEntity>(item).State = entityState;
        }


        public void ApplyCurrentValues<TEntity>(TEntity original, TEntity current)
            where TEntity : class
        {
            //if it is not attached, attach original and set current values
            _mainModuleContext.Entry<TEntity>(original).CurrentValues.SetValues(current);
        }

        public void Commit()
        {
            _mainModuleContext.SaveChanges();
        }

        public void CommitAndRefreshChanges()
        {
            bool saveFailed = false;

            do
            {
                try
                {
                    _mainModuleContext.SaveChanges();
                    saveFailed = false;
                }
                catch (DbEntityValidationException dbEx)
                {
                    foreach (var validationErrors in dbEx.EntityValidationErrors)
                    {
                        foreach (var validationError in validationErrors.ValidationErrors)
                        {
                            Trace.TraceInformation("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage);
                        }
                    }
                }
                catch (DbUpdateConcurrencyException ex)
                {
                    saveFailed = true;
                    ex.Entries.ToList()
                              .ForEach(entry => entry.OriginalValues.SetValues(entry.GetDatabaseValues()));

                }
            } while (saveFailed);

        }

        public void RollbackChanges()
        {
            // set all entities in change tracker 
            // as 'unchanged state'
            _mainModuleContext.ChangeTracker.Entries()
                              .ToList()
                              .ForEach(entry => entry.State = System.Data.EntityState.Unchanged);
        }

        public IEnumerable<TEntity> ExecuteQuery<TEntity>(string sqlQuery, params object[] parameters)
        {
            return _mainModuleContext.Database.SqlQuery<TEntity>(sqlQuery, parameters);
        }

        public int ExecuteCommand(string sqlCommand, params object[] parameters)
        {
            return _mainModuleContext.Database.ExecuteSqlCommand(sqlCommand, parameters);
        }
        #endregion

        public void Dispose()
        {
            _mainModuleContext.Dispose();
        }

    }
}
